﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Workflows.Components.Entities.Distances.Comparers;

namespace Workflows.Components.Entities.Collapses
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class EntityCollapseInstruction:InstructionBase
    {
        #region props
        private string _EntityName;
        /// <summary></summary>
        public string EntityName
        {
            get
            {
                return this._EntityName;
            }
            set
            {
                this._EntityName = value;
            }
        }

        private string _ConnStr;
        /// <summary></summary>
        public string ConnStr
        {
            get
            {
                return this._ConnStr;
            }
            set
            {
                this._ConnStr = value;
            }
        }

        private string _SchemaName;
        /// <summary></summary>
        public string SchemaName
        {
            get
            {
                return this._SchemaName;
            }
            set
            {
                this._SchemaName = value;
            }
        }

        private string _TableName;
        /// <summary></summary>
        public string TableName
        {
            get
            {
                return this._TableName;
            }
            set
            {
                this._TableName = value;
            }
        }

        private string _IDFieldName;
        /// <summary></summary>
        public string IDFieldName
        {
            get
            {
                return this._IDFieldName;
            }
            set
            {
                this._IDFieldName = value;
            }
        }

        private Dictionary<string,double> _DescriptiveAttributes;
        /// <summary></summary>
        public Dictionary<string,double> DescriptiveAttributes
        {
            get
            {
                return this._DescriptiveAttributes;
            }
            set
            {
                this._DescriptiveAttributes = value;
            }
        }

        private Dictionary<string,double> _AssociativeAttributes;
        /// <summary></summary>
        public Dictionary<string,double> AssociativeAttributes
        {
            get
            {
                return this._AssociativeAttributes;
            }
            set
            {
                this._AssociativeAttributes = value;
            }
        }

        private Dictionary<string,double> _PenaltiesWhenNotMatched;
        /// <summary></summary>
        public Dictionary<string,double> PenaltiesWhenNotMatched
        {
            get
            {
                return this._PenaltiesWhenNotMatched;
            }
            set
            {
                this._PenaltiesWhenNotMatched = value;
            }
        }

        private List<string> _UnevenlyDistributedFields;
        /// <summary></summary>
        public List<string> UnevenlyDistributedFields
        {
            get
            {
                return this._UnevenlyDistributedFields;
            }
            set
            {
                this._UnevenlyDistributedFields = value;
            }
        }

        private double _CollapseThreshold;
        /// <summary></summary>
        public double CollapseThreshold
        {
            get
            {
                return this._CollapseThreshold;
            }
            set
            {
                this._CollapseThreshold = value;
            }
        }

        private Dictionary<string, FieldComparerPointer> _FieldComparers;
        /// <summary></summary>
        public Dictionary<string, FieldComparerPointer> FieldComparers
        {
            get
            {
                return this._FieldComparers;
            }
            set
            {
                this._FieldComparers = value;
            }
        }

        private List<List<string>> _OrFilters;
        /// <summary></summary>
        public List<List<string>> OrFilters
        {
            get
            {
                return this._OrFilters;
            }
            set
            {
                this._OrFilters = value;
            }
        }

        private string _OutputTableName;
        /// <summary></summary>
        public string OutputTableName
        {
            get
            {
                return this._OutputTableName;
            }
            set
            {
                this._OutputTableName = value;
            }
        }

        private string _CollapsedTableName;
        /// <summary></summary>
        public string CollapsedTableName
        {
            get
            {
                return this._CollapsedTableName;
            }
            set
            {
                this._CollapsedTableName = value;
            }
        }

        private string _FieldDistributionSourceTableName;
        /// <summary></summary>
        public string FieldDistributionSourceTableName
        {
            get
            {
                return this._FieldDistributionSourceTableName;
            }
            set
            {
                this._FieldDistributionSourceTableName = value;
            }
        }

        private List<List<string>> _SwappedFieldsCollection;
        /// <summary></summary>
        public List<List<string>> SwappedFieldsCollection
        {
            get
            {
                return this._SwappedFieldsCollection;
            }
            set
            {
                this._SwappedFieldsCollection = value;
            }
        }

        private double _AssociateThreshold;
        /// <summary></summary>
        public double AssociateThreshold
        {
            get
            {
                return this._AssociateThreshold;
            }
            set
            {
                this._AssociateThreshold = value;
            }
        }

        private string _OverCollapsedFilePath;
        /// <summary></summary>
        public string OverCollapsedFilePath
        {
            get
            {
                return this._OverCollapsedFilePath;
            }
            set
            {
                this._OverCollapsedFilePath = value;
            }
        }

        private string _UnderCollapsedFilePath;
        /// <summary></summary>
        public string UnderCollapsedFilePath
        {
            get
            {
                return this._UnderCollapsedFilePath;
            }
            set
            {
                this._UnderCollapsedFilePath = value;
            }
        }

        private string _FinalResultFilePath;
        /// <summary></summary>
        public string FinalResultFilePath
        {
            get
            {
                return this._FinalResultFilePath;
            }
            set
            {
                this._FinalResultFilePath = value;
            }
        }

        private List<CompensaterPointer> _Compensaters;
        /// <summary></summary>
        public List<CompensaterPointer> Compensaters
        {
            get
            {
                return this._Compensaters;
            }
            set
            {
                this._Compensaters = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public EntityCollapseInstruction():base(Guid.Empty,Guid.Empty)
        {
            this._EntityName = string.Empty;
            this._ConnStr = string.Empty;
            this._SchemaName = string.Empty;
            this._TableName = string.Empty;
            this._IDFieldName = string.Empty;
            this._DescriptiveAttributes=new Dictionary<string, double>();
            this._AssociativeAttributes=new Dictionary<string, double>();
            this._PenaltiesWhenNotMatched=new Dictionary<string, double>();
            this._UnevenlyDistributedFields=new List<string>();
            this._FieldComparers=new Dictionary<string, FieldComparerPointer>();
            this._OrFilters=new List<List<string>>();
            this._CollapseThreshold = 0.56;
            this._OutputTableName = string.Empty;
            this._CollapsedTableName = string.Empty;
            this._FieldDistributionSourceTableName = string.Empty;
            this._SwappedFieldsCollection=new List<List<string>>();
            this._AssociateThreshold = 0.35;
            this._UnderCollapsedFilePath = string.Empty;
            this._OverCollapsedFilePath = string.Empty;
            this._FinalResultFilePath = string.Empty;
            this._Compensaters = null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        /// <param name="entityName"></param>
        /// <param name="connStr"></param>
        /// <param name="tblName"></param>
        /// <param name="idFieldName"></param>
        /// <param name="descFields"></param>
        /// <param name="assocFields"></param>
        /// <param name="penalties"></param>
        /// <param name="fieldComparers"></param>
        /// <param name="unevenFields"></param>
        /// <param name="collapseThreshold"></param>
        /// <param name="orFilters"></param>
        /// <param name="outputTableName"></param>
        /// <param name="collapseTblName"></param>
        /// <param name="fieldDistSrcTblName"></param>
        /// <param name="swappedFieldColl"></param>
        /// <param name="compensaters"></param>
        /// <param name="finalResultFilePath"></param>
        /// <param name="assocThreshold"></param>
        /// <param name="overCollapsedFilePath"></param>
        /// <param name="underCollapsedFilePath"></param>
        public EntityCollapseInstruction(Guid componentID, Guid jobTicketID,
            string entityName, string connStr, string schemaName, string tblName, string idFieldName,
            Dictionary<string,double> descFields,
            Dictionary<string,double> assocFields,
            Dictionary<string,double> penalties,
            Dictionary<string,FieldComparerPointer> fieldComparers,
            List<string> unevenFields,
            List<List<string>> orFilters, 
            double collapseThreshold,
            string outputTableName, 
            string collapseTblName,
            string fieldDistSrcTblName,
            List<List<string>> swappedFieldColl,
            double assocThreshold,
            string overCollapsedFilePath,
            string underCollapsedFilePath,
            string finalResultFilePath,
            List<CompensaterPointer> compensaters)
            : base(componentID, jobTicketID)
        {
            this._EntityName = entityName;
            this._ConnStr = connStr;
            this._SchemaName = schemaName;
            this._TableName = tblName;
            this._IDFieldName = idFieldName;
            this._DescriptiveAttributes = descFields;
            this._AssociativeAttributes = assocFields;
            this._PenaltiesWhenNotMatched = penalties;
            this._FieldComparers = fieldComparers;
            this._UnevenlyDistributedFields = unevenFields;
            this._OrFilters = orFilters;
            this._CollapseThreshold = collapseThreshold;
            this._OutputTableName = outputTableName;
            this._CollapsedTableName = collapseTblName;
            this._FieldDistributionSourceTableName = fieldDistSrcTblName;
            this._SwappedFieldsCollection = swappedFieldColl;
            this._AssociateThreshold = assocThreshold;
            this._UnderCollapsedFilePath = underCollapsedFilePath;
            this._OverCollapsedFilePath = overCollapsedFilePath;
            this._FinalResultFilePath = finalResultFilePath;
            this._Compensaters = compensaters;
        }
        #endregion

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"EntityName",this.EntityName);
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"ConnStr",this.ConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SchemaName",this.SchemaName);
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"TableName",this.TableName);
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"IDFieldName",this.IDFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc,xNode,"CollapseThreshold",this.CollapseThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "OutputTableName",this.OutputTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapsedTableName", this.CollapsedTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"FieldDistributionSourceTableName",this.FieldDistributionSourceTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "AssociateThreshold", this.AssociateThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"UnderCollapsedFilePath",this.UnderCollapsedFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "OverCollapsedFilePath", this.OverCollapsedFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "FinalResultFilePath", this.FinalResultFilePath);

            if (this.DescriptiveAttributes != null && this.DescriptiveAttributes.Count > 0)
            {
                XmlNode descFieldRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "DescriptiveAttributes");
                foreach (string fieldName in this.DescriptiveAttributes.Keys)
                {
                    XmlNode descFieldNode = XmlDataUtil.AddElement(ref xDoc, descFieldRootNode, "DescriptiveAttribute");
                    XmlDataUtil.UpdateAttribute(ref xDoc, descFieldNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, descFieldNode, "Weight",
                                                this.DescriptiveAttributes[fieldName].ToString());
                }
            }
            if(this.AssociativeAttributes !=null && this.AssociativeAttributes.Count>0)
            {
                XmlNode assocFieldRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "AssociativeAttributes");
                foreach(string fieldName in this.AssociativeAttributes.Keys)
                {
                    XmlNode assocFieldNode = XmlDataUtil.AddElement(ref xDoc, assocFieldRootNode, "AssociativeAttribute");
                    XmlDataUtil.UpdateAttribute(ref xDoc,assocFieldNode,"FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocFieldNode, "Weight",
                                                this.AssociativeAttributes[fieldName].ToString());
                }
            }
            if(this.PenaltiesWhenNotMatched !=null && this.PenaltiesWhenNotMatched.Count>0)
            {
                XmlNode penaltyRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Penalties");
                foreach(string fieldName in this.PenaltiesWhenNotMatched.Keys)
                {
                    XmlNode penaltyNode = XmlDataUtil.AddElement(ref xDoc, penaltyRootNode, "Penalty");
                    XmlDataUtil.UpdateAttribute(ref xDoc,penaltyNode,"FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode,"Penalty", this.PenaltiesWhenNotMatched[fieldName].ToString());
                }
            }
            if(this.FieldComparers !=null && this.FieldComparers.Count>0)
            {
                XmlNode fieldCompareRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "FieldComparers");
                foreach(string fieldName in this.FieldComparers.Keys)
                {
                    XmlNode compareNode = XmlDataUtil.AddElement(ref xDoc, fieldCompareRootNode, "FieldComparer");
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode,"FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode, "AssemblyFileName", this.FieldComparers[fieldName].AssemblyFileName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode, "TypeName",this.FieldComparers[fieldName].TypeFullName);
                }
            }
            if(this.UnevenlyDistributedFields !=null && this.UnevenlyDistributedFields.Count>0)
            {
                XmlNode unevenRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "UnevenlyDistributedFields");
                foreach(string fieldName in this.UnevenlyDistributedFields)
                {
                    XmlNode unevenFieldNode = XmlDataUtil.AddElement(ref xDoc, unevenRootNode,
                                                                     "UnevenlyDistributedField");
                    XmlDataUtil.UpdateAttribute(ref xDoc,unevenFieldNode,"FieldName",fieldName);
                }
            }
            if(this.OrFilters !=null && this.OrFilters.Count>0)
            {
                XmlNode filterRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Filters");
                for(int i=0;i<this.OrFilters.Count;i++)
                {
                    List<string> addFilters = this.OrFilters[i];
                    XmlNode orFilterNode = XmlDataUtil.AddElement(ref xDoc, filterRootNode, "Or");
                    foreach(string fieldName in addFilters)
                    {
                        XmlNode addFilterNode = XmlDataUtil.AddElement(ref xDoc, orFilterNode, "Add");
                        XmlDataUtil.UpdateAttribute(ref xDoc, addFilterNode, "FieldName", fieldName);
                    }
                }
            }
            if(this.SwappedFieldsCollection !=null && this.SwappedFieldsCollection.Count>0)
            {
                XmlNode swapRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "SwappedFieldsCollection");
                for(int i=0;i<this.SwappedFieldsCollection.Count;i++)
                {
                    List<string> swappedFields = this.SwappedFieldsCollection[i];
                    XmlNode swapNode = XmlDataUtil.AddElement(ref xDoc, swapRootNode, "SwappedFields");
                    foreach(string fieldName in swappedFields)
                    {
                        XmlNode swapField = XmlDataUtil.AddElement(ref xDoc, swapNode, "Field");
                        XmlDataUtil.UpdateAttribute(ref xDoc, swapField,"FieldName",fieldName);
                    }
                }
            }

            if (this.Compensaters != null && this.Compensaters.Count > 0)
            {
                XmlNode compensaterRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Compensaters");
                foreach (CompensaterPointer compensater in this.Compensaters)
                {
                    XmlNode compensaterNode = XmlDataUtil.AddElement(ref xDoc, compensaterRootNode, "Compensater");
                    XmlDataUtil.UpdateAttribute(ref xDoc, compensaterNode, "AssemblyName", compensater.AssemblyName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, compensaterNode, "TypeFullName", compensater.TypeFullName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, compensaterNode, "ScanSwap", compensater.CanSwap.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, compensaterNode, "FieldNames", StringUtil.ToString(compensater.FieldNames.ToArray()));
                    XmlDataUtil.UpdateAttribute(ref xDoc, compensaterNode, "CompensateScore", compensater.CompensateScore.ToString());
                }
            }

            return xNode;
        }

        public override InstructionBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                EntityCollapseInstruction instruction = new EntityCollapseInstruction();
                instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
                instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
                instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

                instruction.EntityName = XmlDataUtil.GetAttributeValue(xNode, "EntityName", "");
                instruction.ConnStr = XmlDataUtil.GetAttributeValue(xNode, "ConnStr", "");
                instruction.SchemaName = XmlDataUtil.GetAttributeValue(xNode, "SchemaName", "");
                instruction.TableName = XmlDataUtil.GetAttributeValue(xNode, "TableName", "");
                instruction.IDFieldName = XmlDataUtil.GetAttributeValue(xNode, "IDFieldName", "");
                instruction.CollapseThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "CollapseThreshold", "0"));
                instruction.OutputTableName = XmlDataUtil.GetAttributeValue(xNode, "OutputTableName", "");
                instruction.CollapsedTableName = XmlDataUtil.GetAttributeValue(xNode, "CollapsedTableName", "");
                instruction.FieldDistributionSourceTableName = 
                    XmlDataUtil.GetAttributeValue(xNode,
                    "FieldDistributionSourceTableName", "");
                instruction.AssociateThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "AssociateThreshold", "0.0"));
                instruction.UnderCollapsedFilePath = XmlDataUtil.GetAttributeValue(xNode, "UnderCollapsedFilePath", "");
                instruction.OverCollapsedFilePath = XmlDataUtil.GetAttributeValue(xNode, "OverCollapsedFilePath", "");
                instruction.FinalResultFilePath = XmlDataUtil.GetAttributeValue(xNode, "FinalResultFilePath", "");

                XmlNodeList descNodes = xNode.SelectNodes("DescriptiveAttributes/DescriptiveAttribute");
                if(descNodes !=null && descNodes.Count>0)
                {
                    instruction.DescriptiveAttributes=new Dictionary<string, double>();
                    foreach(XmlNode descNode in descNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(descNode, "FieldName", "");
                        double weight = double.Parse(XmlDataUtil.GetAttributeValue(descNode, "Weight", "0"));
                        instruction.DescriptiveAttributes.Add(fieldName,weight);
                    }
                }
                else
                {
                    instruction.DescriptiveAttributes = null;
                }

                XmlNodeList assocNodes = xNode.SelectNodes("AssociativeAttributes/AssociativeAttribute");
                if(assocNodes !=null && assocNodes.Count>0)
                {
                    instruction.AssociativeAttributes=new Dictionary<string, double>();
                    foreach(XmlNode assocNode in assocNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(assocNode, "FieldName", "");
                        double weight = double.Parse(XmlDataUtil.GetAttributeValue(assocNode, "Weight", "1"));
                        instruction.AssociativeAttributes.Add(fieldName,weight);
                    }
                }
                else
                {
                    instruction.AssociativeAttributes = null;
                }

                XmlNodeList penaltyNodes = xNode.SelectNodes("Penalties/Penalty");
                if(penaltyNodes !=null && penaltyNodes.Count>0)
                {
                    instruction.PenaltiesWhenNotMatched=new Dictionary<string, double>();
                    foreach(XmlNode penaltyNode in penaltyNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(penaltyNode, "FieldName", "");
                        double penalty = double.Parse(XmlDataUtil.GetAttributeValue(penaltyNode, "Penalty", "0.0"));
                        instruction.PenaltiesWhenNotMatched.Add(fieldName,penalty);
                    }
                }
                else
                {
                    instruction.PenaltiesWhenNotMatched = null;
                }

                XmlNodeList compareNodes = xNode.SelectNodes("FieldComparers/FieldComparer");
                if(compareNodes !=null && compareNodes.Count>0)
                {
                    instruction.FieldComparers=new Dictionary<string, FieldComparerPointer>();
                    foreach(XmlNode compareNode in compareNodes)
                    {
                        string fieldName = XmlDataUtil.GetAttributeValue(compareNode, "FieldName", "");
                        string assemFileName = XmlDataUtil.GetAttributeValue(compareNode, "AssemblyFileName", "");
                        string typeName = XmlDataUtil.GetAttributeValue(compareNode, "TypeName", "");
                        FieldComparerPointer comparer=new FieldComparerPointer();
                        comparer.AssemblyFileName = assemFileName;
                        comparer.TypeFullName = typeName;
                        instruction.FieldComparers.Add(fieldName, comparer);
                    }
                }

                XmlNodeList unevenFieldNodes = xNode.SelectNodes("UnevenlyDistributedFields/UnevenlyDistributedField");
                if(unevenFieldNodes !=null && unevenFieldNodes.Count>0)
                {
                    instruction.UnevenlyDistributedFields=new List<string>();
                    foreach(XmlNode unevenNode in unevenFieldNodes)
                    {
                        instruction.UnevenlyDistributedFields.Add(XmlDataUtil.GetAttributeValue(unevenNode, "FieldName",
                                                                                                ""));
                    }
                }
                else
                {
                    instruction.UnevenlyDistributedFields = null;
                }

                XmlNodeList orNodes = xNode.SelectNodes("Filters/Or");
                if(orNodes !=null && orNodes.Count>0)
                {
                    instruction.OrFilters=new List<List<string>>();
                    foreach(XmlNode orNode in orNodes)
                    {
                        List<string> addFilters=new List<string>();
                        foreach(XmlNode addNode in orNode.ChildNodes)
                        {
                            string fieldName = XmlDataUtil.GetAttributeValue(addNode, "FieldName", "");
                            addFilters.Add(fieldName);
                        }
                        if(addFilters.Count>0)
                        {
                            instruction.OrFilters.Add(addFilters);
                        }
                    }
                }
                XmlNodeList swapNodes = xNode.SelectNodes("SwappedFieldsCollection/SwappedFields");
                if(swapNodes !=null && swapNodes.Count>0)
                {
                    instruction.SwappedFieldsCollection=new List<List<string>>();
                    foreach(XmlNode swapNode in swapNodes)
                    {
                        List<string> swappedFields=new List<string>();
                        foreach(XmlNode fieldNode in swapNode.ChildNodes)
                        {
                            string fieldName = XmlDataUtil.GetAttributeValue(fieldNode, "FieldName", "");
                            swappedFields.Add(fieldName);
                        }
                        instruction.SwappedFieldsCollection.Add(swappedFields);
                    }
                }

                XmlNodeList compensateNodes = xNode.SelectNodes("Compensaters/Compensater");
                if (compareNodes != null && compareNodes.Count > 0)
                {
                    instruction.Compensaters = new List<CompensaterPointer>();
                    foreach (XmlNode compensateNode in compareNodes)
                    {
                        CompensaterPointer compensater = new CompensaterPointer();
                        compensater.AssemblyName = XmlDataUtil.GetAttributeValue(compensateNode, "AssemblyName", "");
                        compensater.TypeFullName = XmlDataUtil.GetAttributeValue(compensateNode, "TypeFullName", "");
                        compensater.CanSwap = bool.Parse(XmlDataUtil.GetAttributeValue(compensateNode, "CanSwap", false.ToString()));
                        compensater.FieldNames = new List<string>();
                        string[] fieldNames = StringUtil.Split(XmlDataUtil.GetAttributeValue(compensateNode, "FieldNames", ""));
                        if (fieldNames != null && fieldNames.Length > 0)
                        {
                            compensater.FieldNames.AddRange(fieldNames);
                        }
                        compensater.CompensateScore = double.Parse(XmlDataUtil.GetAttributeValue(compensateNode, "CompensateScore", "0.0"));
                        instruction.Compensaters.Add(compensater);
                    }
                }
                else
                {
                    instruction.Compensaters = null;
                }

                return instruction;
            }
            else
            {
                throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
            }
        }
    }
}
